package com.xyzwps.webfluxjava.controller;

import com.google.common.collect.ImmutableMap;
import com.xyzwps.webfluxjava.common.exception.BizException;
import lombok.Builder;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Mono;

import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.io.IOException;
import java.rmi.activation.UnknownGroupException;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.Map;
import java.util.Optional;

@RequestMapping("/ex")
@RestController
@Slf4j
public class ExRestController {

    @GetMapping("/path-vars/{id}")
    public Mono<ExampleDto> pathVars(@PathVariable("id") Long id) {
        log.info("path-vars: id={}", id);
        return Mono.just(ExampleDto.newInstance());
    }

    @PostMapping("/http-status")
    @ResponseStatus(HttpStatus.CREATED)
    public Mono<ExampleDto> httpStatus() {
        return Mono.just(ExampleDto.newInstance());
    }

    @PostMapping(path = "/content-nego", consumes = MediaType.APPLICATION_JSON_VALUE)
    public Mono<ExampleDto> contentNego(@RequestBody ExampleDto dto) {
        return Mono.just(dto);
    }

    @GetMapping(path = "/query-params", params = "ver=v2")
    public Mono<ExampleDto> queryParams() {
        return Mono.just(ExampleDto.newInstance());
    }

    @GetMapping(path = "/headers", headers = "ver=v2")
    public Mono<Map<String, Object>> headers(@RequestHeader("ver") String ver) {
        return Mono.just(ImmutableMap.of("version", ver));
    }

    @SuppressWarnings("all") // idea 会警告任何 optional 的使用
    @GetMapping("/optional")
    public Mono<Map> optiona(@RequestParam("ver") Optional<String> version) {
        return Mono.just(ImmutableMap.of("version", version.orElse("v1")));
    }

    // eg: GET /ex/matrix-vars/lalila;size=12;page=2/sth/more;color=red
    @GetMapping("/matrix-vars/{xyz}/sth/{more}")
    public Mono<Map<String, Object>> matrix(
            @MatrixVariable(name = "page", pathVar = "xyz") Integer page,
            @MatrixVariable(name = "size", pathVar = "xyz") Integer size,
            @PathVariable("xyz") String xyz,
            @MatrixVariable(name = "color", pathVar = "more") String color) {
        return Mono.just(ImmutableMap.of("size", size, "page", page,"xyz", xyz, "color", color));
    }

    @PostMapping("/mono-req")
    public Mono<ExampleDto> contentNego(@RequestBody Mono<ExampleDto> mono) {
        return mono;
    }

    /**
     * 这里不能用 {@link BindingResult} 了。🙁
     *
     * <a href="https://docs.spring.io/spring/docs/current/spring-framework-reference/web-reactive.html#webflux-ann-requestbody">点此查看相关文档</a>
     *
     * TODO: 解释为啥不可用
     */
    @PostMapping("/validate")
    public Mono<Map<String, Object>> valid(@Valid @RequestBody ExampleLoginDto dto) {
        return Mono.just(ImmutableMap.of("username", dto.getUsername()));
    }


    @GetMapping("/except/{number}")
    public Mono<?> except(@PathVariable("number") int number) {
        Exception ex;
        switch (number) {
            case 1: ex = BizException.badRequestError("拽没了"); break;
            case 2: ex = new IOException("胡乱抛个异常"); break;
            default: ex = new UnknownGroupException("看看谁来处理");
        }
        return Mono.error(ex);
    }



}

@Builder
@Data
class ExampleDto {
    private String title;
    private String content;
    private Date createTime;
    private LocalDateTime lastUpdateTime;

    public static ExampleDto newInstance() {
        return ExampleDto.builder()
                .title("Hello world")
                .content("<p>Hello world</p>")
                .createTime(new Date())
                .lastUpdateTime(LocalDateTime.now())
                .build();
    }
}

@Data
class ExampleLoginDto {
    @NotNull(message = "请填写用户名")
    private String username;

    @NotNull(message = "请填写密码")
    private String password;

    @NotNull(message = "请填写验证码")
    @Size(min = 6, max = 6, message = "验证码只能包含 6 个字符")
    private String vcode;
}